import { App, Directive, DirectiveBinding, createApp, h } from 'vue';
import { Circle } from 'vant';
import { calculateValue, rgbTest, getColorByProgress } from '@/utils/index';

/**
 * 扩展的HTMLElement接口，包含指令所需的附加属性
 */
interface ElType extends HTMLElement {
    __vueStart__?: (e: MouseEvent | TouchEvent) => void; // 开始事件处理函数
    __vueCancel__?: () => void; // 取消事件处理函数
    __vueProgress__?: HTMLElement; // 进度条元素
    __vuePressTimer__?: ReturnType<typeof setTimeout> | null; // 长按计时器
    __vueProgressInterval__?: ReturnType<typeof setInterval> | null; // 进度条更新间隔计时器
    __vueCircleApp__?: App<Element>; // 圆形进度条实例
    __vueCircleContainer__?: HTMLElement; // 圆形进度条容器
    __vueClickTimer__?: ReturnType<typeof setTimeout> | null; // 短时间点击计时器
}

/**
 * 长按指令的选项接口
 */
interface LongPressOptions {
    callback: (e: MouseEvent | TouchEvent) => void; // 长按回调函数
    pressType?: 'line' | 'circle'; // 进度条类型
    duration?: number; // 长按持续时间
    hasBorder?: boolean; // 是否显示边框
    pressColor?: string; // 按压颜色
    layerColor?: string; // 层颜色
    xy?: boolean; // 是否沿x轴
    width?: string; // 进度条宽度
    tblr?: number; // 从上下左右开始
    clickDuration?: number; // 短时间点击持续时间
}

/**
 * 定义长按指令
 */
export const longpress: Directive = {
    /**
     * 在元素绑定指令之前执行
     * @param el - 绑定指令的元素
     * @param binding - 指令绑定对象，包含传入的值和修饰符
     */
    beforeMount(el: ElType, binding: DirectiveBinding<LongPressOptions>) {
        const options = binding.value;

        // 检查传入的回调函数是否有效
        if (typeof options.callback !== 'function') {
            throw new Error('The value of v-longpress must be a function');
        }

        // 初始化赋值
        el.__vuePressTimer__ = null;
        el.__vueProgressInterval__ = null;
        el.__vueClickTimer__ = null;

        // 从传入的选项中获取或设置默认值
        const pressType = options.pressType ?? 'line';  // 默认类型是'line'
        const duration = options.duration ?? 1000;  // 默认持续时间是1000毫秒
        const hasBorder = options.hasBorder ?? true;  // 默认有边框
        const pressColor = options.pressColor ?? 'rgb(66,185,131)';  // 默认按压颜色
        const layerColor = options.layerColor ?? '#eee';  // 默认层颜色
        const xy = options.xy ?? true;  // 默认沿x轴
        const width = pressType == 'line' ? (options.width ?? '4') + 'px' : options.width ?? '20';  // 默认宽度4px或20px
        const tblr = options.tblr ?? 0;  // 默认从下到上
        const clickDuration = options.clickDuration ?? 200;  // 默认短时间点击持续时间是200毫秒

        el.style.position = 'relative';

        // 验证颜色格式是否为rgb
        if (!rgbTest(pressColor)) {
            throw new Error('The color value of v-longpress must be a rgb');
        }

        // 去掉颜色值的最后一个字符
        const realPressColor = pressColor.substring(0, pressColor.length - 1);

        /**
         * 长按开始事件处理函数
         * @param e - 事件对象
         */
        const start = (e: MouseEvent | TouchEvent) => {
            // 仅在鼠标左键按下时触发
            if (e instanceof MouseEvent && e.button !== 0) {
                return;
            }
            // 防止多次触发
            if (el.__vueClickTimer__ === null) {
                el.__vueClickTimer__ = setTimeout(() => {
                    // 防止多次触发
                    if (el.__vuePressTimer__ === null) {
                        let progress = 0;

                        /**
                         * 根据类型创建进度条或圆形进度条
                         */
                        if (pressType === 'line') {
                            // 创建线性进度条
                            const progressBar = document.createElement('div');
                            progressBar.style.position = 'absolute';
                            if (xy) {
                                progressBar.style.height = width;
                                progressBar.style.width = '0%';
                                progressBar.style.left = '0';
                            } else {
                                progressBar.style.height = '0%';
                                progressBar.style.width = width;
                                progressBar.style.bottom = '0';
                            }

                            progressBar.style.backgroundColor = `${realPressColor},0)`;
                            progressBar.style.transition = 'width 0.2s linear';
                            // 创建线性进度条父级
                            const progressParent = document.createElement('div');
                            progressParent.style.position = 'absolute';
                            progressParent.style.backgroundColor = layerColor;
                            progressParent.style.pointerEvents = 'none'
                            if (xy) {
                                progressParent.style.height = width;
                                progressParent.style.width = '100%';
                                progressParent.style.left = '0';
                                tblr === 1 ? progressParent.style.top = '0' : progressParent.style.bottom = '0';
                            } else {
                                progressParent.style.height = '100%';
                                progressParent.style.width = width;
                                progressParent.style.bottom = '0';
                                tblr === 1 ? progressParent.style.right = '0' : progressParent.style.left = '0';
                            }

                            // 将进度条添加到元素中
                            progressParent.appendChild(progressBar);
                            el.appendChild(progressParent);
                            el.__vueProgress__ = progressParent;
                            xy ? progressBar.style.width = '0%' : progressBar.style.height = '0%';
                            hasBorder ? progressParent.style.border = 'dashed .1px grey' : '';

                            // 设置计时器，当计时结束时触发回调
                            el.__vuePressTimer__ = setTimeout(() => {
                                handler(e);
                                clearInterval(el.__vueProgressInterval__!);
                                xy ? progressBar.style.width = '100%' : progressBar.style.height = '100%'
                                progressBar.style.backgroundColor = getColorByProgress(realPressColor, 100);
                            }, duration);

                            // 设置进度条的更新间隔
                            el.__vueProgressInterval__ = setInterval(() => {
                                progress += 1000 / duration;
                                progressBar.style.backgroundColor = getColorByProgress(realPressColor, progress);
                                xy ? progressBar.style.width = `${progress}%` : progressBar.style.height = `${progress}%`
                            }, 10);
                        } else if (pressType === 'circle') {
                            // 创建圆形进度条
                            if (!el.__vueCircleContainer__) {
                                const circleContainer = document.createElement('div');
                                circleContainer.className = 'absolute-translateX'
                                el.appendChild(circleContainer);
                                const circleSize = el.getBoundingClientRect().height;
                                const circleApp = createApp({
                                    data() {
                                        return {
                                            rate: 0,
                                            color: realPressColor + ',0)'
                                        };
                                    },
                                    render() {
                                        return h(Circle, {
                                            size: circleSize,
                                            strokeWidth: calculateValue(circleSize),
                                            layerColor: layerColor,
                                            color: this.color,
                                            currentRate: this.rate
                                        });
                                    }
                                });

                                circleApp.mount(circleContainer);
                                el.__vueCircleApp__ = circleApp;
                                el.__vueCircleContainer__ = circleContainer;
                            }
                            el.__vueCircleContainer__.style.display = 'inline';
                            el.__vuePressTimer__ = setTimeout(() => {
                                handler(e);
                                clearInterval(el.__vueProgressInterval__!);
                                el.__vueCircleApp__!._instance!.data.rate = 100;
                                el.__vueCircleApp__!._instance!.data.color = `${realPressColor},1)`;
                            }, duration);

                            el.__vueProgressInterval__ = setInterval(() => {
                                progress += 1000 / duration;
                                el.__vueCircleApp__!._instance!.data.color = getColorByProgress(realPressColor, progress);
                                el.__vueCircleApp__!._instance!.data.rate = progress;
                            }, 10);
                        }
                    }
                }, clickDuration);
            }
        };

        /**
         * 长按取消事件处理函数
         */
        const cancel = () => {
            if (el.__vuePressTimer__ !== null) {
                clearTimeout(el.__vuePressTimer__);
                el.__vuePressTimer__ = null;
            }
            if (el.__vueProgressInterval__ !== null) {
                clearInterval(el.__vueProgressInterval__);
                el.__vueProgressInterval__ = null;
            }

            if (el.__vueClickTimer__ !== null) {
                clearTimeout(el.__vueClickTimer__);
                el.__vueClickTimer__ = null;
            }
            if (pressType === 'line' && el.__vueProgress__) {
                const progressBar = el.__vueProgress__.querySelector('div');
                if (progressBar) {
                    xy ? progressBar.style.width = '0%' : progressBar.style.height = '0%';
                    progressBar.style.backgroundColor = getColorByProgress(realPressColor);
                }
                el.__vueProgress__.style.border = '';
                el.__vueProgress__.style.backgroundColor = '';
            } else if (pressType === 'circle' && el.__vueCircleApp__) {
                el.__vueCircleApp__!._instance!.data.rate = 0;
                el.__vueCircleApp__!._instance!.data.color = getColorByProgress(realPressColor);
                el.__vueCircleContainer__!.style.display = 'none';
            }
        };

        /**
         * 调用传入的回调函数
         * @param e - 事件对象
         */
        const handler = (e: MouseEvent | TouchEvent) => {
            options.callback(e);
        };

        // 将事件处理函数绑定到元素上
        el.__vueStart__ = start;
        el.__vueCancel__ = cancel;

        el.addEventListener('mousedown', start);
        el.addEventListener('touchstart', start, { passive: true });
        el.addEventListener('click', cancel);
        el.addEventListener('mouseout', cancel);
        el.addEventListener('touchend', cancel);
        el.addEventListener('touchcancel', cancel);
    },
    /**
     * 在元素解绑指令时执行
     * @param el - 绑定指令的元素
     */
    unmounted(el: ElType) {
        // 从元素的自定义属性中获取 start 和 cancel 函数
        const start = el.__vueStart__;
        const cancel = el.__vueCancel__;

        // 确保函数存在再移除事件监听器
        if (start) {
            el.removeEventListener('mousedown', start);
            el.removeEventListener('touchstart', start);
        }
        if (cancel) {
            el.removeEventListener('click', cancel);
            el.removeEventListener('mouseout', cancel);
            el.removeEventListener('touchend', cancel);
            el.removeEventListener('touchcancel', cancel);
        }

        // 移除进度条或圆形进度条
        if (el.__vueProgress__) {
            el.removeChild(el.__vueProgress__);
        }
        if (el.__vueCircleContainer__) {
            el.removeChild(el.__vueCircleContainer__);
        }

        // 清除计时器
        if (el.__vuePressTimer__) {
            clearTimeout(el.__vuePressTimer__);
        }

        if (el.__vueProgressInterval__) {
            clearInterval(el.__vueProgressInterval__);
        }

        if (el.__vueClickTimer__) {
            clearTimeout(el.__vueClickTimer__);
        }

        // 删除自定义属性
        delete el.__vueStart__;
        delete el.__vueCancel__;
        delete el.__vueProgress__;
        delete el.__vueCircleApp__;
        delete el.__vueCircleContainer__;
        delete el.__vuePressTimer__;
        delete el.__vueProgressInterval__;
        delete el.__vueClickTimer__;
    }
};